home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Network Support Library
/
RoseWare - Network Support Library.iso
/
apidev
/
spxcon.arc
/
CONNECT.C
next >
Wrap
C/C++ Source or Header
|
1987-12-02
|
18KB
|
489 lines
/*****************************************************************************
*
* Program Name: CONNECT
*
* Filename: connect.c
*
* Date Created: December 1, 1987
*
* Programmers: Bryan Sparks
* Software Consultant
* API Consulting
* Novell Inc.
*
* Files used: connecta.asm, myalloc.c
*
* Comments:
* This program is to be used as examples, only. It shows how to
* set-up and use the SPX communication protocol. This program
* was written in conjunction with an article contained in the
* "Developer's Update", in the January/February issue; the "Update
* is also available on NetWire. There are library calls made in this
* program that are currently unavailable to the public. The libraries
* could easily be duplicated, however, by obtaining the document
* "Novell Peer-To-Peer Communication Protocols". (See the "Developer's
* Update" article for more details on how to obtain the document.) The
* library would contain function calls following the specifications
* described in the Peer-To-Peer document.
*
* This program is written in Microsoft C 4.0 (see the MAKEFILE). You
* will notice that I use the compiler option Ox. This suppresses
* stack and alias checking. I had to do this because Microsoft C kept
* telling me that I was over-running my stack and I knew I wasn't. I now
* just tell Microsoft C not to check the stack. There may be better
* solutions to this problem but this worked for me.
*
****************************************************************************/
#include <stdio.h>
#include <nit.h>
#include "connect.h"
int Sender=FALSE, Receiver=FALSE;
long ReceiveStats=0, SendStats=0;
/* The assignment of sockets is made through the API Consulting group of */
/* Novell. In this example I use this socket. It could have been */
/* any socket. If you are writing a program for NetWare and that program */
/* uses IPX or SPX be sure that you get a socket assignment from Novell */
/* immediately before the release of your product. */
unsigned int ConnectSocket=0x802C;
main(argc,argv)
int argc;
char *argv[];
{
int ccode=0;
int connectionID, connectionNumber;
switch (argc) {
case 2:
if ( (strcmp( argv[1], "sender" )) == 0 )
Sender = TRUE;
if ( (strcmp( argv[1], "receiver" )) == 0 )
Receiver = TRUE;
if ( !((Sender == TRUE) || (Receiver == TRUE)) ) {
printf("\nusage:\tconnect <sender>");
printf("\n \tconnect <receiver>\n");
exit (0);
}
break;
default:
printf("\nusage:\tconnect <sender>");
printf("\n \tconnect <receiver>\n");
exit (0);
break;
}
printf("This program is for example of SPX code\n\n");
/* IPXInitialize will go out and get the address of IPX and SPX services. */
/* In 2.1 it is necessary to find out where IPX/SPX services is. */
/* This is done by loading AX with 7A00h and issuing an INT 2Fh */
/* The address of IPX/SPX will be returned as a pointer in ES:DI */
/* All IPX or SPX calls are done by doing a far call to this address. */
IPXInitialize();
/* Open Socket "ConnectSocket" as a socket that will be closed when */
/* the program terminates; indicated by the 0x00. */
if ( (ccode = IPXOpenSocket( &ConnectSocket, 0x00 ) ) != 0 ) {
printf("\rError occurred during open socket. ccode: %0x\n", ccode );
exit (-1);
}
GiveSPXAnListenECBPool();
/* One of the complexities of peer-to-peer communications is the problem */
/* of resolving addresses. What is the address of the peer to which */
/* I want to send to. I get this information by having the sending */
/* side supply the connection number of the receiver. I then */
/* use this connection number to find his address. This is done by */
/* issuing a function call ( Map a Connection to an Internetwork Address) */
/* E3h (13h). (See the function call reference for details of the call.) */
/* The other means of resloving address resloution will be the topic */
/* of one of my articles in the March/April Developer's Update. */
if ( Sender ) {
printf("Enter the connection number to connect to: ");
connectionNumber = atoi( gets() );
/* This is a function that sets up the connection with the */
/* waiting receiver. The connection ID is returned. */
connectionID = SetUpSenderConnection( connectionNumber );
StartSending( connectionNumber, connectionID );
}
else {
/* Must be the receiving side. */
printf("Hit any key to quit waiting.\n");
printf("Waiting for Establish Connection.");
/* This procedure sets up the Receive side of the connection. */
connectionID = SetUpReceiverConnection();
printf("\nHit any key to quit this connection.\n");
/* Once the connection is established the sender starts sending. */
/* Upon reception of packets the Event Service Routine associated */
/* with every receive increments "ReceiveStats". I then print */
/* that number here. This number indicates the number of packets */
/* that have been received at any one time. */
while ( !kbhit() ) {
printf("%08lx\r", ReceiveStats);
}
}
}
/*
** This procedure sets up the connection. It takes in the connection number
** of the peer to which the connection should be established. It then finds
** the address of the peer by issuing function call E3h (13h), Map a Connection
** to an Internetwork Address.
**
** For a detail description of the fields required in the ECB to set up a
** connection see the document "Peer-To-Peer Communication Protocols".
*/
int SetUpSenderConnection( connectionNumber )
int connectionNumber;
{
SPXPacket *wPacket;
ECB *wECB;
char far *farCharPointer;
int ccode, connectionID, transportTime;
if ( (wECB = (ECB *)myalloc( 1, sizeof( ECB )) ) == NULL ) {
printf("Out of memory allocating space for ECB.\n");
exit (-1);
}
if ( (wPacket = (SPXPacket *)myalloc( 1, sizeof( SPXPacket )) ) == NULL ) {
printf("Out of memory allocating space for SPXPacket.\n");
exit (-1);
}
wPacket->PacketLength = IntSwap( sizeof( SPXPacket ) );
wPacket->PacketType = 5; /* SPX Packet */
wPacket->Destination.Socket = ConnectSocket;
wECB->ECBSocket = ConnectSocket;
wECB->FragmentCount = 1;
farCharPointer = (char far *)wPacket;
wECB->FragmentDescriptor[0].Address = farCharPointer;
wECB->FragmentDescriptor[0].Size = sizeof( SPXPacket );
/* This function finds the address of someone given the connection */
/* number. I put this address directly in the SPX Packet header. */
MapConnectionNumberToAddress( (unsigned char)connectionNumber,
wPacket->Destination.Network, wPacket->Destination.Node );
/* This IPX function finds the nearest router to use to get the the */
/* destination. As you are aware NetWare supports an internetwork. */
/* To get to a destination there may be various routes. This call */
/* finds the nearest route. This LocalTarget may be a fileserver or */
/* a bridge. We put the address of the LocalTarget in the ECB */
/* Immediate Address fields. */
IPXGetLocalTarget( (unsigned char *)wPacket->Destination.Network,
(unsigned char *)wECB->ImmediateAddress, transportTime );
/* For a more detailed description of the SPX function call. See the */
/* document "Peer-To-Peer Communication Protocols" */
ccode = SPXEstablishConnection( (char)5, (char)0, (int *)&connectionID,
wECB );
if ( ccode != 0 ) {
printf("\nError establishing connection. ccode = %0x\n", ccode);
exit (-1);
}
/* I loop here because I shouldn't touch the ECB until IPX/SPX has */
/* finished with it. IPX/SPX is done with the ECB when the InUseFlag */
/* is set to zero. */
while ( wECB->InUseFlag ) {
IPXRelinquishControl();
}
switch ( wECB->CompletionCode ) {
case 0x00:
printf("Connection established. connectionID: %0x\n", connectionID );
break;
case 0xED:
printf("Couldn't establish connection. Was the receiver ready?\n");
exit (-1);
break;
default:
printf("Error occurred during establishing connection: %0x\n", ccode);
exit (-1);
break;
}
return ( connectionID );
}
/*
** This is the reverse side of the SetUpSenderConnection() procedure.
** This procedure simple waits for the sender to "call."
*/
int SetUpReceiverConnection()
{
SPXPacket *wPacket;
ECB *wECB;
char far *farCharPointer;
int connectionID;
if ( (wECB = (ECB *)myalloc( 1, sizeof( ECB )) ) == NULL ) {
printf("Out of memory allocating space for ECB.\n");
exit (-1);
}
if ( (wPacket = (SPXPacket *)myalloc( 1, sizeof( SPXPacket )) ) == NULL ) {
printf("Out of memory allocating space for SPXPacket.\n");
exit (-1);
}
wPacket->PacketLength = IntSwap( sizeof( SPXPacket ) );
wPacket->PacketType = (char)5; /* SPX Packet */
wECB->ECBSocket = ConnectSocket;
wECB->FragmentCount = 1;
farCharPointer = (char far *)wPacket;
wECB->FragmentDescriptor[0].Address = farCharPointer;
wECB->FragmentDescriptor[0].Size = sizeof( SPXPacket );
/* For a more detailed description of the SPX function call. See the */
/* document "Peer-To-Peer Communication Protocols" */
SPXListenForConnection( (char)0, (char)0, wECB );
/* I loop here because I shouldn't touch the ECB until IPX/SPX has */
/* finished with it. IPX/SPX is done with the ECB when the InUseFlag */
/* is set to zero. */
while ( wECB->InUseFlag ) {
IPXRelinquishControl();
if ( kbhit() )
exit (0);
}
switch ( wECB->CompletionCode ) {
case 0x00:
/* This connection number is stored in the first two bytes of the */
/* ECB field, IPX Workspace. I copy the ID into ConnectionID. */
memcpy( (char *)&connectionID, (char *)wECB->IPXWorkspace, 2 );
printf("\rConnection established. connectionID: %0x\n", connectionID );
return (connectionID);
break;
default:
printf("\rError occurred in connectionECB. CompletionCode: %0x\n",
wECB->CompletionCode);
exit (-1);
break;
}
}
/*
** This is the procedure that sends packets to the receiver on the
** connection that was established earlier.
*/
StartSending( connectionNumber, connectionID )
int connectionNumber;
unsigned connectionID;
{
ECB *sECB;
SPXPacket *sPacket;
char far *farCharPointer, *data;
int transportTime;
void SendESRHandler();
if ( (sECB = (ECB *)myalloc( 1, sizeof( ECB )) ) == NULL ) {
printf("Out of memory allocating space for ECB.\n");
exit (-1);
}
if ( (sPacket = (SPXPacket *)myalloc( 1, sizeof( SPXPacket )) ) == NULL ) {
printf("Out of memory allocating space for SPXPacket.\n");
exit (-1);
}
data = (char *)myalloc( 1, DATASIZE );
/* This is the information that I am sending. This was just an example */
/* program anyway; the information wasn't really important. */
strcpy( data, "xxxxxxxxxx" );
/* Assign the send Event Service Routine */
farCharPointer = (char far *)SendESRHandler;
sECB->ESRAddress = farCharPointer;
sECB->ECBSocket = ConnectSocket;
/* I have two fragments. The first fragment contains the SPXPacket Header */
/* and the second fragment contains the data. This was an arbritrary */
/* decision and could be done in other ways. */
sECB->FragmentCount = 2;
farCharPointer = (char far *)sPacket;
sECB->FragmentDescriptor[0].Address = farCharPointer;
sECB->FragmentDescriptor[0].Size = sizeof( SPXPacket );
farCharPointer = (char far *)data;
sECB->FragmentDescriptor[1].Address = farCharPointer;
sECB->FragmentDescriptor[1].Size = DATASIZE;
sPacket->PacketType = (char)5;
sPacket->PacketLength = IntSwap( sizeof( SPXPacket ) );
/* Find the address of the peer to whom I am suppose to be sending. */
MapConnectionNumberToAddress( (unsigned char)connectionNumber,
sPacket->Destination.Network, sPacket->Destination.Node );
/* Get the nearest router and place the address of the router in the */
/* ECB field "ImmediateAddress" */
IPXGetLocalTarget( (unsigned char *)sPacket->Destination.Network,
(unsigned char *)sECB->ImmediateAddress, transportTime );
sPacket->Destination.Socket = ConnectSocket;
printf("\nHit any key to quit this connection.\n");
while ( !kbhit() ) {
SPXSendSequencedPacket( connectionID, sECB );
/* I loop here because I shouldn't touch the ECB until IPX/SPX has */
/* finished with it. IPX/SPX is done with the ECB when the InUseFlag */
/* is set to zero. */
while ( sECB->InUseFlag )
IPXRelinquishControl();
switch ( sECB->CompletionCode ) {
case 0x00:
break;
case 0xED: /* Connection died. */
printf("\nConnection failed. Exiting.\n");
exit (-1);
break;
case 0xEE: /* Connection was terminated. */
printf("\nConnection was terminated by the other side.\n");
exit (0);
break;
default:
printf("\nError occurred during my send loop. ccode: %0x\n",
sECB->CompletionCode );
exit (-1);
break;
}
printf("%08lx\r", SendStats+1 );
}
/* To clean up we should terminate the connection. */
TerminateTheConnection( connectionID );
}
/*
** This procedure puts some Event Control Blocks (ECBs) out for SPX to use.
** These ECBs will later be used, if the program is acting as receiver, to
** listen for incoming packets. When a packet is received SPX will activate
** the associated Event Service Routine (ReceiveESRHandler). In this case
** the Event Service Routine is contained in an assembly program
** (Connecta.asm) which will call a 'C' procedure (ReceiveESR()).
** The assembly module simply pushes the address of the ECB on the stack
** then calls the 'C' module.
**
** See the document "Peer-To-Peer" protocols for a description of what
** fields must be initialized in the ECB before issuing an
** SPXListenForSequencedPacket();
**
** NOTE: Some ECBs must be allocated even if you are the sender. SPX needs
** them for its pool of availible ECBs. This is IMPORTANT!! You must
** have this pool of ECBs available for SPX. SPX uses them to
** implement the flow control and guarnteed delivery services.
** A pool of 2 would be the minimum but more is suggested. In this
** program I use a pool of 5.
*/
GiveSPXAnListenECBPool()
{
ECB *wECB;
SPXPacket *sPacket;
char far *farCharPointer, *data;
int i=0;
void ReceiveESRHandler();
while ( i++ != 5 ) {
if ( (wECB = (ECB *)myalloc( 1, sizeof( ECB )) ) == NULL ) {
printf("Out of memory allocating space for ECB.\n");
exit (-1);
}
if ( (sPacket = (SPXPacket *)myalloc( 1, sizeof( SPXPacket )) ) == NULL ) {
printf("Out of memory allocating space for SPXPacket.\n");
exit (-1);
}
data = (char *)myalloc( 1, DATASIZE );
farCharPointer = (char far *)ReceiveESRHandler;
wECB->ESRAddress = farCharPointer;
wECB->ECBSocket = ConnectSocket;
/* I will have two fragments in this program. This is not a */
/* requirement. You could implement with 1 fragment or more */
/* than 2. It is up to the programmer. You will notice that */
/* the first fragment contains the SPXPacket header. This is a */
/* requirement. My second fragment will contain the data I am */
/* sending. In this program this is bogus information. */
wECB->FragmentCount = 2;
farCharPointer = (char far *)sPacket;
wECB->FragmentDescriptor[0].Address = farCharPointer;
wECB->FragmentDescriptor[0].Size = sizeof( SPXPacket );
farCharPointer = (char far *)data;
wECB->FragmentDescriptor[1].Address = farCharPointer;
wECB->FragmentDescriptor[1].Size = DATASIZE;
sPacket->PacketType = (char)5;
sPacket->PacketLength = IntSwap( sizeof( SPXPacket ) );
SPXListenForSequencedPacket( wECB );
}
}
/*
** This function terminates a connection.
*/
TerminateTheConnection( connectionID )
int connectionID;
{
ECB *wECB;
SPXPacket *sPacket;
char far *farCharPointer;
if ( (wECB = (ECB *)myalloc( 1, sizeof( ECB )) ) == NULL ) {
printf("Out of memory allocating space for ECB.\n");
exit (-1);
}
if ( (sPacket = (SPXPacket *)myalloc( 1, sizeof( SPXPacket )) ) == NULL ) {
printf("Out of memory allocating space for SPXPacket.\n");
exit (-1);
}
wECB->ECBSocket = ConnectSocket;
wECB->FragmentCount = 1;
farCharPointer = (char far *)sPacket;
wECB->FragmentDescriptor[0].Address = farCharPointer;
wECB->FragmentDescriptor[0].Size = sizeof( SPXPacket );
sPacket->PacketType = (char)5; /* SPX Packet */
sPacket->PacketLength = IntSwap( sizeof( SPXPacket ) );
SPXTerminateConnection( connectionID, wECB );
/* I loop here because I shouldn't touch the ECB until IPX/SPX has */
/* finished with it. IPX/SPX is done with the ECB when the InUseFlag */
/* is set to zero. */
while ( wECB->InUseFlag )
IPXRelinquishControl();
}
/*
** This Event Service Routine is activated when a packet is received on
** the appropiate socket.
**
** Remember that this program is called from an assembly program
** called "ReceiveESRHandler". That procedure puts the address
** of the ECB on the stack and calls this procedure. This is just
** the way I decided to implement it. There are other options
** available to you.
*/
void ReceiveESR( wECB )
ECB *wECB;
{
/* I simply increment a counter and put the ECB in the Listen pool again */
ReceiveStats++;
SPXListenForSequencedPacket( wECB );
}
/*
** Increments a global variable.
*/
void SendESR( wECB )
ECB *wECB;
{
SendStats++;
}